﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeuralNetworks.ActivationFunctions;
using NeuralNetworks.Layers;

namespace NeuralNetworks.Networks
{
    public class ActivationNetwork : Network
    {
        public ActivationLayer inputLayer, outputLayer;
        public ActivationLayer[] hiddenLayers;

        public Layer InputLayer { get { return inputLayer; } }
        public Layer OutputLayer { get { return outputLayer; } }

        public int HiddenLayersCount { get { return hiddenLayers.Length; } }

        public new int LayersCount { get { return hiddenLayers.Length + 1; } }

        public ActivationLayer this[int index]
        {
            get
            {
                if (index < HiddenLayersCount)
                {
                    return hiddenLayers[index];
                }
                return outputLayer;
            }
        }

        public ActivationNetwork(int inputSize, int nHidden, int outputSize, int[] hiddenSize, IActivationFunction f)
        {
            if ((inputSize < 1) || (hiddenSize.Length < nHidden) || (outputSize < 1) || (nHidden < 0) || (f == null))
            { throw (new NeuralNetworksException()); };

            // create layers
            inputLayer = new ActivationLayer(inputSize, new IdentityFunction(), "input");
            
            hiddenLayers = new ActivationLayer[nHidden];
            for (int i = 0; i < nHidden; i++)
            {
                hiddenLayers[i] = new ActivationLayer(hiddenSize[i], f, "hidden#" + i.ToString());
            }

            outputLayer = new ActivationLayer(outputSize, f, "output");
            // connect layers
            if (nHidden != 0)
            {
                outputLayer.ConnectWithLayer(hiddenLayers[nHidden - 1]);
                for (int i = nHidden - 1; i > 0; i--)
                {
                    hiddenLayers[i].ConnectWithLayer(hiddenLayers[i - 1]);
                }
                hiddenLayers[0].ConnectWithLayer(inputLayer);
            }
            else
            {
                outputLayer.ConnectWithLayer(inputLayer);
            }
        }


        new public double[] Compute(double[] x)
        {
            if ((x == null) || (x.Length != inputLayer.Neurons.Count))
            { throw (new NeuralNetworksException()); };

            inputLayer.SetOutputs(x);
            for (int i = 0; i < hiddenLayers.Length; i++)
            {
                hiddenLayers[i].Compute();
            }
            outputLayer.Compute();
            return outputLayer.Outputs();
        }

        public override void RandomWeights(double min, double max)
        {
            Random rand = new Random();
            inputLayer.RandomWeights(rand);
            for (int i = 0; i < hiddenLayers.Length; i++)
            {
                hiddenLayers[i].RandomWeights(rand);
            }
            outputLayer.RandomWeights(rand);
        }

        public override void ZeroWeights()
        {
            inputLayer.ZeroWeights();
            for (int i = 0; i < hiddenLayers.Length; i++)
            {
                hiddenLayers[i].ZeroWeights();
            }
            outputLayer.ZeroWeights();
        }

    }
}
